package pl.poznan.put.cs.idss.viamdp.core.algorithm;

import java.util.LinkedList;
import java.util.List;

import pl.poznan.put.cs.idss.viamdp.core.structures.Map;

/**
 * Implementacja Value Iterations Algorithm rozwiązująca MDP.
 * 
 * @author Kornel Lewandowski
 * 
 */
public class VIA implements IAlgorithm {

	private Map worldMap;
	private List<Double> errorsHistory = new LinkedList<Double>();
	private boolean finish = false;

	public void setMap(Map map) {
		worldMap = map;
	}

	public boolean checkIfEnd() {

		if (finish) {
			return true;
		}

		errorsHistory.add(getMaxError());

		for (int i = 0; i < worldMap.getSizeX(); i++) {
			for (int j = 0; j < worldMap.getSizeY(); j++) {
				if (worldMap.getFieldsArray()[i][j].checkIfRewardChange() == true) {
					return false;
				}
			}
		}

		finish = true;
		return true;
	}

	public void doStep() {

		if (checkIfEnd() == true) {
			getCurrentState().printMapAsText();
		} else {
			for (int i = 0; i < worldMap.getSizeX(); i++) {
				for (int j = 0; j < worldMap.getSizeY(); j++) {
					worldMap.getFieldsArray()[i][j].calculateCurrentReward();
				}
			}
		}

		worldMap.incrementIteration();

	}

	public Map getCurrentState() {
		return worldMap;
	}

	public double getMaxError() {
		double error = 0.0;
//		System.out.println("-----");
//		System.out.println("-ITERATION-" + worldMap.getIteration());
		for (int i = 0; i < worldMap.getSizeX(); i++) {
			for (int j = 0; j < worldMap.getSizeY(); j++) {
				List<Double> history = worldMap.getFieldsArray()[i][j]
						.getRewardsHistory();
				if (history.size() > 1) {
					double previous = history.get(history.size() - 2);
					double current = history.get(history.size() - 1);
					double difference = Math.abs(current - previous);
//					System.out.println("D: " + difference);
					error = Math.max(difference, error);
				} else {
					error = history.get(history.size() - 1);
				}
			}
		}
//		System.out.println("MAX: " + error);
//		System.out.println();
		return error;
	}

	public boolean isFinish() {
		return finish;
	}

	public void setFinish(boolean finish) {
		this.finish = finish;
	}

}
